using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Xml.Serialization;
using WozLib.UI.Ink.SketchCommands;

namespace WozLib.Net
{
  public class WozServer
  {
    public const int PORT = 7825;
    public const int HTTP_FILE_SERVER_PORT = 7826;

    private TcpListener _TcpListener;

    private Thread _ServerThread;

    private StreamWriter _ourWriter = null;

    Socket _ClientSocket = null;

    /// <summary>
    /// Is true if any WozServer (really a singleton anyway) is in use
    /// </summary>
    public static bool IsConnected
    {
      get { return _IsConnected; }
    } private static bool _IsConnected;


    public WozServer()
    {
      _TcpListener = new TcpListener(IPAddress.Any,PORT);
      _IsConnected = true;
    }

    public void Start()
    {
      _ServerThread = new Thread(new ThreadStart(Run));
      _ServerThread.Start();
    }

    public void Stop()
    {
      // close down connection gracefully
      if (_TcpListener != null)
      {
        _TcpListener.Stop();
      }
      if (_ClientSocket != null)
      {
        try
        {
          _ClientSocket.Disconnect(true);
        }
        catch (Exception ex)
        {
          Debug.WriteLine(ex.ToString());
        }
      }

      // then kill the thread
      if (_ServerThread != null)
      {
        _ServerThread.Abort();
      }
    }

    public string AddrString
    {
      get 
      {
        string toReturn = "cannot find addr";

        try
        {
          toReturn = Dns.GetHostName();
        }
        catch (Exception ex)
        {
          Debug.WriteLine(ex.ToString());
        }

        return toReturn;
      }
    }


    public delegate void WozClientCommandsReceivedDelegate(SketchCommandCollection wozClientCommands);

    public event WozClientCommandsReceivedDelegate WozClientCommandsReceived;

    public void SendWozClientCommands(SketchCommandCollection wozClientCommands)
    {
      if (_ourWriter != null && wozClientCommands != null)
      {
        lock (_ourWriter)
        {
          try
          {
            SketchCommandCollection toSerialize = wozClientCommands;

            // Insert code to set properties and fields of the object.
            XmlSerializer mySerializer = new XmlSerializer(typeof(SketchCommandCollection));

            // To write to a file, create a StreamWriter object.
            StringWriter sw = new StringWriter();
            mySerializer.Serialize(sw, toSerialize);
            sw.Close();

            string toSend = sw.ToString();

            _ourWriter.WriteLine(toSend.Length.ToString());
            _ourWriter.Write(toSend);
            _ourWriter.Flush();
          }
          catch (Exception ex)
          {
            Debug.WriteLine(" SEND FAILED!!! " + ex.ToString());
          }
        }
      }
    }

    public delegate void ConnectedDelegate();
    public event ConnectedDelegate Connected;

    private void Run()
    {
      try
      {
        _TcpListener.Start();
        _ClientSocket = _TcpListener.AcceptSocket();

        if (!(_ClientSocket.Connected))
        {
          Debug.WriteLine("cannot start server because clientSocket wasn't connected");
        }
        else
        {
          NetworkStream ourStream = new NetworkStream(_ClientSocket);
          StreamReader ourReader = new StreamReader(ourStream);
          char[] xmlChars = null;
          _ourWriter = new StreamWriter(ourStream);
          Debug.WriteLine("client connection established");

          if (Connected != null)
          {
            Connected();
          }

          while (true)
          {
            try
            {
              string strXmlLength = ourReader.ReadLine();
              //Debug.WriteLine("Incoming message \"{0}\"", strXmlLength);

              int xmlLength = int.Parse(strXmlLength);

              xmlChars = new char[xmlLength];

              int lengthRead = 0;
              while (lengthRead < xmlLength)
              {
                lengthRead += ourReader.Read(xmlChars, lengthRead, xmlLength - lengthRead);
                //if (lengthRead != xmlLength)
                //{
                //  Debug.WriteLine("incorrect message length, expected " + xmlLength.ToString() + " but got " + lengthRead.ToString());
                //}
              }

              XmlSerializer mySerializer = new XmlSerializer(typeof(SketchCommandCollection));

              StringReader sr = new StringReader(new string(xmlChars));

              SketchCommandCollection unserialized = (SketchCommandCollection)mySerializer.Deserialize(sr);

              if (WozClientCommandsReceived != null)
              {
                WozClientCommandsReceived(unserialized);
              }
            }
            catch (Exception ex)
            {
              Debug.WriteLine("server closing because of an error: " + ex.ToString());
              if (xmlChars != null)
              {
                string s = new string(xmlChars);
                Console.WriteLine(s);
              }
              //ClientSocket.Close();
              return;
            }
          }
        }
      }
      catch (Exception ex)
      {
        Debug.WriteLine(ex.ToString());
      }
    }

    /// <summary>
    /// Used for getting the hostname of the server.
    /// Works from client and server perspectives.
    /// </summary>
    /// <returns>IP Address or Hostname of the WozServer</returns>
    internal static string GetHostName()
    {
      if (IsConnected || WozClient.HostName == null)
      {
        return Dns.GetHostName();
      }
      else
      {
        return WozClient.HostName;
      }
    }
  }
}
